package com.maple.base.viewModel

import android.text.TextUtils
import androidx.lifecycle.*
import com.maple.base.app.BaseApp
import com.maple.model.error.ERROR
import com.maple.utils.manager.SingleLiveEvent
import com.maple.utils.network.error.ExceptionHandle
import com.maple.utils.network.error.ResponseThrowable
import com.maple.utils.util.HttpResponseUtils
import com.maple.utils.util.LogUtils
import com.maple.utils.util.NetworkUtil
import com.maple.model.resp.BaseResp
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import java.net.ConnectException
import java.util.concurrent.TimeoutException

abstract class BaseVM: ViewModel(), DefaultLifecycleObserver {

    val app: BaseApp by lazy {
        BaseApp.instance
    }

    val defUI: UIChange by lazy { UIChange() }

    fun getClassName(): String {
        return this.javaClass.name
    }

    /**
     * 所有网络请求都在 viewModelScope 域中启动，当页面销毁时会自动
     * 调用ViewModel的  #onCleared 方法取消所有协程
     */
    fun launchUI(block: suspend CoroutineScope.() -> Unit, err: suspend CoroutineScope.(e: String?) -> Unit) = viewModelScope.launch {
        try {
            block()
        } catch (e: TimeoutException) {
            err(ERROR.TIMEOUT_ERROR.getValue())
        } catch (e: ConnectException) {
            err(ERROR.NETWORD_UNCONNECTED.getValue())
        } catch (e: Exception) {
            LogUtils.logGGQ("===Exception====>>>${e.message}")
            err(ERROR.NETWORD_ERROR.getValue())
        }
    }

    fun launchUI2(block: suspend CoroutineScope.() -> Unit,err: suspend CoroutineScope.(e: String?) -> Unit) = viewModelScope.launch {
       runCatching {
           block()
       }.onFailure {
           LogUtils.logGGQ("launchUI2 error:${it.message}")
           err(it.message)
       }
    }
    /**
     * 用流的方式进行网络请求
     */
    fun <T> launchFlow(block: suspend () -> T): Flow<T> {
        return channelFlow {
            send(block())
        }
    }

    /**
     * UI事件
     */
    inner class UIChange {
        val toastEvent by lazy { SingleLiveEvent<String>() }

        val emptyEvent by lazy { SingleLiveEvent<Any>() }

        fun onToast(msg: String?) {
            toastEvent.postValue(msg)
        }

        fun onEmpty(msg: String? = "暂无数据！") {
            emptyEvent.postValue(msg)
        }

        val showDialog by lazy { SingleLiveEvent<Any>() }
        fun onShowDialog() {
            showDialog.call()
        }

        val dismissDialog by lazy { SingleLiveEvent<Any>() }
        fun onDismissDialog () {
            dismissDialog.call()
        }
    }

    /**
     *  不过滤请求结果
     * @param block 请求体
     * @param error 失败回调
     * @param complete  完成回调（无论成功失败都会调用）
     * @param isShowDialog 是否显示加载框
     */
    fun launchGo(
        isShowDialog: Boolean = true,
        isShowToast: Boolean = true,
        block: suspend CoroutineScope.() -> Unit,
        error: suspend CoroutineScope.(ResponseThrowable) -> Unit = {
            LogUtils.logGGQ("--isShowToast-->${isShowToast}")
            LogUtils.logGGQ("--error-->${it.errMsg}")
            if(isShowToast) defUI.onToast(it.errMsg)
        },
        complete: suspend CoroutineScope.() -> Unit = {}
    ) {
        if (!NetworkUtil.isConnected()) {
            defUI.onToast(ERROR.NETWORD_ERROR.getValue())
            return
        }
        if (isShowDialog) defUI.onShowDialog()
        launchUI(block = {
            handleException(
                withContext(Dispatchers.IO) { block },
                {
                    if(isShowDialog) {
                        defUI.onDismissDialog()
                    }
                    error(it)
                },
                {
                    complete()
                    if(isShowDialog) {
                        defUI.onDismissDialog()
                    }
                }
            )
        },err = {
            if(isShowDialog) {
                defUI.onDismissDialog()
            }
            LogUtils.logGGQ("==err=launchGo=>>>${it.toString()}")
        })
    }

    /**
     * 过滤请求结果，其他全抛异常
     * @param block 请求体
     * @param success 成功回调
     * @param error 失败回调
     * @param complete  完成回调（无论成功失败都会调用）
     * @param isShowDialog 是否显示加载框
     */
    fun launchOnlyResult(
        isShowDialog: Boolean = true,
        isShowToast: Boolean = true,
        isCareDialog: Boolean = false,
        block: suspend CoroutineScope.() -> BaseResp,
        success: (BaseResp) -> Unit,
        error: (ResponseThrowable) -> Unit = {
            LogUtils.logGGQ("--isShowToast--->${isShowToast}")
            LogUtils.logGGQ("--error--->${it.errMsg}")
            defUI.onDismissDialog()
            if(isShowToast) defUI.onToast(it.errMsg)
        },
        complete: suspend CoroutineScope.() -> Unit = {
            if(!isCareDialog && isShowDialog) {
                defUI.onDismissDialog()
            }
        }
    ) {
        if (!NetworkUtil.isConnected()) {
            defUI.onToast(ERROR.NETWORD_ERROR.getValue())
            return
        }
        if (isShowDialog) defUI.onShowDialog()
        launchUI2(block = {
            handleException<BaseResp>(
                { withContext(Dispatchers.IO) { block() } },
                { res ->
                    executeResponse(res) {
                        success(it)
                    }
                },
                {
                    if(isShowDialog) {
                        defUI.onDismissDialog()
                    }
                    error(it)
                },
                {
                    complete()
                    if(!isCareDialog && isShowDialog) {
                        defUI.onDismissDialog()
                    }
                }
            )
        },err = {
            if(isShowDialog) {
                defUI.onDismissDialog()
            }
            val errorHandle = ExceptionHandle.handleException(it.toString())
            error(errorHandle)
            if(isShowToast) {
                defUI.onToast(errorHandle.errMsg)
            }
            LogUtils.logGGQ("==err=launchOnlyResult=>>>${errorHandle.errMsg}")
        })
    }

    /**
     * 请求结果过滤
     */
    private suspend fun executeResponse(
        response: BaseResp,
        success: suspend CoroutineScope.(BaseResp) -> Unit
    ) {
        coroutineScope {
            if (isSuccess(response)) {
                success(response)
            } else {
                onResultError(response)
            }
        }
    }

    private fun onResultError(resp: BaseResp) {
        if(!TextUtils.isEmpty(resp.message)) {
            resp.msg = resp.message
        }
        if(TextUtils.isEmpty(resp.msg)) {
            resp.msg = "操作失败！"
        }
        LogUtils.logGGQ("----http--->${resp.msg}")
        if(handleCodeError(resp.code, resp.msg)){
            return
        }
        throw ResponseThrowable(resp.code, resp.msg)
    }

    /// 处理接口返回不统一的判断情况
    open fun isSuccess(resp: BaseResp): Boolean {
        return resp.success
    }

    /// 处理异常code的情况
    open fun handleCodeError(code: String?,msg: String?): Boolean{
        return HttpResponseUtils.handleErrorCode(code,msg)
    }


    /**
     * 异常统一处理
     * 不过滤请求结果使用
     */
    private suspend fun handleException(
        block: suspend CoroutineScope.() -> Unit,
        error: suspend CoroutineScope.(ResponseThrowable) -> Unit,
        complete: suspend CoroutineScope.() -> Unit
    ) {
        try {
            coroutineScope {
                try {
                    block()
                    LogUtils.logGGQ("-->>-N-T-<<")
                } catch (e: ResponseThrowable) {
                    error(ExceptionHandle.handleException(e, e))
                } finally {
                    complete()
                }
            }
        } catch (e: ResponseThrowable) {
            e.fillInStackTrace()
            LogUtils.logGGQ("异常--e->${e.message}")
            error(ExceptionHandle.handleException(e, e))
        }
    }


    /**
     * 异常统一处理
     * 过滤请求结果使用
     */
    private suspend fun <T> handleException(
        block: suspend CoroutineScope.() -> BaseResp,
        success: suspend CoroutineScope.(BaseResp) -> Unit,
        error: suspend CoroutineScope.(ResponseThrowable) -> Unit,
        complete: suspend CoroutineScope.() -> Unit
    ) {
        try {
            coroutineScope {
                try {
                    success(block())
                    LogUtils.logGGQ("-->>-T-T-<<")
                } catch (e: ResponseThrowable) {
                    error(ExceptionHandle.handleException(e, e))
                } finally {
                    complete()
                }
            }
        } catch (e: ResponseThrowable) {
            e.fillInStackTrace()
            LogUtils.logGGQ("异常--e->${e.message}")
            error(ExceptionHandle.handleException(e, e))
        }
    }

    override fun onCleared() {
        super.onCleared()
        LogUtils.logGGQ("VM ->> onCleared")
    }
}